/*
 * Copyright (C) 2012 Andrew Neal Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

package com.andrew.apollo.ui.activities;

import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.internal.view.SupportMenuItem;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.MenuItemCompat.OnActionExpandListener;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.SearchView.OnCloseListener;
import android.support.v7.widget.SearchView.OnQueryTextListener;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import com.andrew.apollo.IApolloService;
import com.andrew.apollo.MusicPlaybackService;
import com.andrew.apollo.MusicStateListener;
import com.andrew.apollo.R;
import com.andrew.apollo.ui.fragments.SearchResultsFragment;
import com.andrew.apollo.utils.ApolloUtils;
import com.andrew.apollo.utils.Lists;
import com.andrew.apollo.utils.MusicUtils;
import com.andrew.apollo.utils.MusicUtils.ServiceToken;
import com.andrew.apollo.utils.NavUtils;
import com.andrew.apollo.utils.PreferenceUtils;
import com.andrew.apollo.utils.ThemeUtils;
import com.andrew.apollo.widgets.PlayPauseButton;
import com.andrew.apollo.widgets.RepeatButton;
import com.andrew.apollo.widgets.ShuffleButton;
import com.nineoldandroids.view.animation.AnimatorProxy;
import com.sothree.slidinguppanel.SlidingUpPanelLayout;

import java.lang.ref.WeakReference;
import java.util.ArrayList;

import static com.andrew.apollo.utils.MusicUtils.mService;

/**
 * A base {@link android.support.v4.app.FragmentActivity} used to update the bottom bar and bind to
 * Apollo's service.
 * <p/>
 * {@link HomeActivity} extends from this skeleton.
 *
 * @author Andrew Neal (andrewdneal@gmail.com)
 */
public abstract class BaseActivity extends ActionBarActivity implements
        ServiceConnection {

    public static final String BACK_STACK_PREFS = ":settings:prefs";
    /**
     * Playstate and meta change listener
     */
    private final ArrayList<MusicStateListener> mMusicStateListener = Lists
            .newArrayList();
    /**
     * Opens the album profile of the currently playing album
     */
    private final View.OnClickListener mOpenCurrentAlbumProfile = new View.OnClickListener() {

        /**
         * {@inheritDoc}
         */
        @Override
        public void onClick(final View v) {
            if (MusicUtils.getCurrentAudioId() != -1) {
                NavUtils.openAlbumProfile(BaseActivity.this,
                        MusicUtils.getAlbumName(), MusicUtils.getArtistName(),
                        MusicUtils.getCurrentAlbumId());
            } else {
                MusicUtils.shuffleAll(BaseActivity.this);
            }
            if (BaseActivity.this instanceof ProfileActivity) {
                finish();
            }
        }
    };
    /**
     * Opens the now playing screen
     */
    private final View.OnClickListener mOpenNowPlaying = new View.OnClickListener() {

        /**
         * {@inheritDoc}
         */
        @Override
        public void onClick(final View v) {
            if (MusicUtils.getCurrentAudioId() != -1) {
                // NavUtils.openAudioPlayer(BaseActivity.this);
                if (!mSlidingUpPanelLayout.isPanelExpanded())
                    mSlidingUpPanelLayout.expandPanel();
            } else {
                MusicUtils.shuffleAll(BaseActivity.this);
            }
        }
    };
    /**
     * Theme resources
     */
    protected ThemeUtils mResources;
    OnCloseListener onCloseListener = new OnCloseListener() {

        @Override
        public boolean onClose() {
            // TODO Auto-generated method stub
            return false;
        }
    };
    /**
     * The service token
     */
    private ServiceToken mToken;
    /**
     * Play and pause button (BAB)
     */
    private PlayPauseButton mPlayPauseButton;
    /**
     * Repeat button (BAB)
     */
    private RepeatButton mRepeatButton;
    /**
     * Shuffle button (BAB)
     */
    private ShuffleButton mShuffleButton;
    /**
     * Track name (BAB)
     */
    private TextView mTrackName;
    /**
     * Artist name (BAB)
     */
    private TextView mArtistName;
    /**
     * Album art (BAB)
     */
    private ImageView mAlbumArt;
    /**
     * Broadcast receiver
     */
    private PlaybackStatus mPlaybackStatus;
    /**
     * Keeps track of the back button being used
     */
    private boolean mIsBackPressed = false;
    private SlidingUpPanelLayout mSlidingUpPanelLayout;
    private int mSlidingUpPanelLayoutHeight;
    private Toolbar mToolbar;
    private View mBottomActionBarParent;
    // Progess
    private SeekBar mProgress;
    private AudioPlayerFragment mAudioPlayerFragment;
    SlidingUpPanelLayout.PanelSlideListener panelSlideListener = new SlidingUpPanelLayout.PanelSlideListener() {

        @Override
        public void onPanelSlide(View panel, float slideOffset) {
            if (mBottomActionBarParent.getVisibility() != View.VISIBLE)
                mBottomActionBarParent.setVisibility(View.VISIBLE);
            Log.i("way", "slideOffset = " + slideOffset);
            // mBottomActionBarParent.setAlpha(1 - slideOffset);
            int mainViewOffset = (int) (panel.getHeight() * Math.max(
                    slideOffset, 0));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
                mBottomActionBarParent.setTranslationY(mainViewOffset);
            else
                AnimatorProxy.wrap(mBottomActionBarParent).setTranslationY(
                        mainViewOffset);
        }

        @Override
        public void onPanelCollapsed(View panel) {
            // TODO Auto-generated method stub
            mBottomActionBarParent.setVisibility(View.VISIBLE);
        }

        @Override
        public void onPanelExpanded(View panel) {
            // TODO Auto-generated method stub
            mBottomActionBarParent.setVisibility(View.GONE);
            if (mService != null && mAudioPlayerFragment != null)
                mAudioPlayerFragment.onServiceConnected(mService);
        }

        @Override
        public void onPanelAnchored(View panel) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onPanelHidden(View panel) {
            // TODO Auto-generated method stub

        }

    };
    private SupportMenuItem mSearchMenuItem;
    private SearchView mSearchView;
    private boolean mSearchMenuItemExpanded = false;
    OnActionExpandListener onActionExpandListener = new OnActionExpandListener() {

        @Override
        public boolean onMenuItemActionExpand(MenuItem item) {
            if (item.getItemId() == mSearchMenuItem.getItemId()) {
                switchToSearchResultsFragmentIfNeeded();
            }

            return true;
        }

        @Override
        public boolean onMenuItemActionCollapse(MenuItem item) {
            if (item.getItemId() == mSearchMenuItem.getItemId()) {
                if (mSearchMenuItemExpanded) {
                    revertToInitialFragment();
                }
            }
            return true;
        }
    };
    private SearchResultsFragment mSearchResultsFragment;
    private ViewGroup mContent;
    private String mSearchQuery;

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Initialze the theme resources
        mResources = new ThemeUtils(this);

        // Set the overflow style
        // mResources.setOverflowStyle(this);

        // Fade it in
        overridePendingTransition(android.R.anim.fade_in,
                android.R.anim.fade_out);

        // Control the media volume
        setVolumeControlStream(AudioManager.STREAM_MUSIC);

        // Bind Apollo's service
        mToken = MusicUtils.bindToService(this, this);

        // Initialize the broadcast receiver
        mPlaybackStatus = new PlaybackStatus(this);

        // Set the layout
        setContentView(setContentView());
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
        mToolbar.setBackgroundColor(PreferenceUtils.getInstance(this)
                .getDefaultThemeColor(this));
        mResources.transWindows(this, PreferenceUtils.getInstance(this)
                .getDefaultThemeColor(this));
        // transWindows();
        // mResources.themeActionBar(mToolbar, getString(R.string.app_name));
        // Initialze the bottom action bar
        initBottomActionBar();
        initSlider();
        mContent = (ViewGroup) findViewById(R.id.activity_base_content);
        if (savedInstanceState == null) {
            mAudioPlayerFragment = new AudioPlayerFragment();
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.playFrame, mAudioPlayerFragment).commit();
        }
    }

    public void initSlider() {
        mSlidingUpPanelLayout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout);
        // mSlidingUpPanelLayout.setCoveredFadeColor(color)
        // mSlidingUpPanelLayout.setDragView(mBottomActionBarParent);
        // mSlidingUpPanelLayout
        // .setDragView(findViewById(R.id.bottom_action_bar_parent));
        mSlidingUpPanelLayoutHeight = mSlidingUpPanelLayout.getPanelHeight();
        mSlidingUpPanelLayout.setPanelSlideListener(panelSlideListener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onServiceConnected(final ComponentName name,
                                   final IBinder service) {
        mService = IApolloService.Stub.asInterface(service);
        // Set the playback drawables
        updatePlaybackControls();
        // Current info
        updateBottomActionBarInfo();
        // Update the favorites icon
        invalidateOptionsMenu();
        if (mAudioPlayerFragment != null)
            mAudioPlayerFragment.onServiceConnected(mService);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onServiceDisconnected(final ComponentName name) {
        mService = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        // Search view
        getMenuInflater().inflate(R.menu.search, menu);
        // Settings
        getMenuInflater().inflate(R.menu.activity_base, menu);
        // Theme the search icon
        // mResources.setSearchIcon(menu);
        mSearchMenuItem = (SupportMenuItem) menu.findItem(R.id.menu_search);
        mSearchView = (SearchView) MenuItemCompat
                .getActionView(mSearchMenuItem);
        if (mSearchMenuItem == null || mSearchView == null) {
            return false;
        }
        if (mSearchResultsFragment != null) {
            mSearchResultsFragment.setSearchView(mSearchView);
        }
        // Add voice search
        final SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        final SearchableInfo searchableInfo = searchManager
                .getSearchableInfo(getComponentName());
        mSearchView.setSearchableInfo(searchableInfo);
        // Perform the search
        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(final String query) {
                // Open the search activity
                // NavUtils.openSearch(BaseActivity.this, query);
                // return true;
                switchToSearchResultsFragmentIfNeeded();
                mSearchQuery = query;
                return mSearchResultsFragment.onQueryTextSubmit(query);
            }

            @Override
            public boolean onQueryTextChange(final String newText) {
                mSearchQuery = newText;
                if (mSearchResultsFragment == null) {
                    return false;
                }
                return mSearchResultsFragment.onQueryTextChange(newText);
            }
        });
        mSearchMenuItem
                .setSupportOnActionExpandListener(onActionExpandListener);
        mSearchView.setOnCloseListener(onCloseListener);
        if (mSearchMenuItemExpanded) {
            mSearchMenuItem.expandActionView();
        }
        return super.onCreateOptionsMenu(menu);
    }

    private void switchToSearchResultsFragmentIfNeeded() {
        if (mSearchResultsFragment != null) {
            return;
        }
        Fragment current = getSupportFragmentManager().findFragmentById(
                R.id.activity_base_search);
        if (current != null && current instanceof SearchResultsFragment) {
            mSearchResultsFragment = (SearchResultsFragment) current;
        } else {
            mSearchResultsFragment = (SearchResultsFragment) switchToFragment(
                    SearchResultsFragment.class.getName(), null, false, true,
                    R.string.menu_search, null, true);
        }
        mSearchResultsFragment.setSearchView(mSearchView);
        mSearchMenuItemExpanded = true;
        findViewById(R.id.activity_base_search).setVisibility(View.VISIBLE);
    }

    public void revertToInitialFragment() {
        // mNeedToRevertToInitialFragment = false;
        mSearchResultsFragment = null;
        mSearchMenuItemExpanded = false;
        // getFragmentManager().popBackStackImmediate(BACK_STACK_PREFS,
        // FragmentManager.POP_BACK_STACK_INCLUSIVE);
        if (mSearchMenuItem != null) {
            mSearchMenuItem.collapseActionView();
        }
        findViewById(R.id.activity_base_search).setVisibility(View.INVISIBLE);
    }

    /**
     * Switch to a specific Fragment with taking care of validation, Title and
     * BackStack
     */
    private Fragment switchToFragment(String fragmentName, Bundle args,
                                      boolean validate, boolean addToBackStack, int titleResId,
                                      CharSequence title, boolean withTransition) {
        // if (validate && !isValidFragment(fragmentName)) {
        // throw new
        // IllegalArgumentException("Invalid fragment for this activity: "
        // + fragmentName);
        // }
        Fragment f = Fragment.instantiate(this, fragmentName, args);
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();
        transaction.replace(R.id.activity_base_search, f);
        if (withTransition) {
            // TransitionManager.beginDelayedTransition(mContent);
        }
        if (addToBackStack) {
            // transaction.addToBackStack(BACK_STACK_PREFS);
        }
        if (titleResId > 0) {
            transaction.setBreadCrumbTitle(titleResId);
        } else if (title != null) {
            transaction.setBreadCrumbTitle(title);
        }
        transaction.commitAllowingStateLoss();
        getSupportFragmentManager().executePendingTransactions();
        return f;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_settings:
                // Settings
                NavUtils.openSettings(this);
                return true;
            default:
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    public void reload() {

        Intent intent = getIntent();
        overridePendingTransition(0, 0);
        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
        finish();

        overridePendingTransition(0, 0);
        startActivity(intent);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onResume() {
        super.onResume();
        if (PreferenceUtils.getInstance(this).getChangeThemeColor(this)) {
            PreferenceUtils.getInstance(this).setChangeThemeColor(false);
            reload();
        }
        // Set the playback drawables
        updatePlaybackControls();
        // Current info
        updateBottomActionBarInfo();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onStart() {
        super.onStart();
        final IntentFilter filter = new IntentFilter();
        // Play and pause changes
        filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED);
        // Shuffle and repeat changes
        filter.addAction(MusicPlaybackService.SHUFFLEMODE_CHANGED);
        filter.addAction(MusicPlaybackService.REPEATMODE_CHANGED);
        // Track changes
        filter.addAction(MusicPlaybackService.META_CHANGED);
        // Update a list, probably the playlist fragment's
        filter.addAction(MusicPlaybackService.REFRESH);
        registerReceiver(mPlaybackStatus, filter);
        MusicUtils.notifyForegroundStateChanged(this, true);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onStop() {
        super.onStop();
        MusicUtils.notifyForegroundStateChanged(this, false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Unbind from the service
        if (mToken != null) {
            MusicUtils.unbindFromService(mToken);
            mToken = null;
        }

        // Unregister the receiver
        try {
            unregisterReceiver(mPlaybackStatus);
        } catch (final Throwable e) {
            //$FALL-THROUGH$
        }

        // Remove any music status listeners
        mMusicStateListener.clear();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onBackPressed() {
        if (mSlidingUpPanelLayout.isPanelExpanded())
            mSlidingUpPanelLayout.collapsePanel();
        else
            super.onBackPressed();
        mIsBackPressed = true;
    }

    /**
     * Initializes the items in the bottom action bar.
     */
    private void initBottomActionBar() {
        mBottomActionBarParent = findViewById(R.id.bottom_action_bar_parent);
        // mProgress = (SeekBar) findViewById(android.R.id.progress);
        // Play and pause button
        mPlayPauseButton = (PlayPauseButton) findViewById(R.id.action_button_play);
        // Shuffle button
        mShuffleButton = (ShuffleButton) findViewById(R.id.action_button_shuffle);
        // Repeat button
        mRepeatButton = (RepeatButton) findViewById(R.id.action_button_repeat);
        // Track name
        mTrackName = (TextView) findViewById(R.id.bottom_action_bar_line_one);
        // Artist name
        mArtistName = (TextView) findViewById(R.id.bottom_action_bar_line_two);
        // Album art
        mAlbumArt = (ImageView) findViewById(R.id.bottom_action_bar_album_art);
        // Open to the currently playing album profile
        // mAlbumArt.setOnClickListener(mOpenCurrentAlbumProfile);
        // Bottom action bar
        final LinearLayout bottomActionBar = (LinearLayout) findViewById(R.id.bottom_action_bar);
        // Display the now playing screen or shuffle if this isn't anything
        // playing
        bottomActionBar.setOnClickListener(mOpenNowPlaying);
    }

    /**
     * Sets the track name, album name, and album art.
     */
    private void updateBottomActionBarInfo() {

        toggleBottomActionBar();

        // Set the track name
        mTrackName.setText(MusicUtils.getTrackName());
        // Set the artist name
        mArtistName.setText(MusicUtils.getArtistName());
        // Set the album art
        ApolloUtils.getImageFetcher(this).loadCurrentArtistImage(mAlbumArt);
        // mAudioPlayerFragment.updateNowPlayingInfo();
    }

    /**
     * Sets the correct drawable states for the playback controls.
     */
    private void updatePlaybackControls() {
        // Set the play and pause image
        mPlayPauseButton.updateState();
        // Set the shuffle image
        mShuffleButton.updateShuffleState();
        // Set the repeat image
        mRepeatButton.updateRepeatState();
    }

    /**
     * @param status The {@link MusicStateListener} to use
     */
    public void setMusicStateListenerListener(final MusicStateListener status) {
        if (status != null) {
            mMusicStateListener.add(status);
        }
    }

    private void startSlideToAnim(final boolean isGone) {
        TranslateAnimation translateAnimation = new TranslateAnimation(0.0F,
                0.0F, mSlidingUpPanelLayoutHeight, 0.0F);
        if (isGone)
            translateAnimation = new TranslateAnimation(0.0F, 0.0F, 0.0F,
                    mSlidingUpPanelLayoutHeight);
        translateAnimation.setDuration(800L);
        mBottomActionBarParent.startAnimation(translateAnimation);
        mBottomActionBarParent.setVisibility(isGone ? View.GONE
                : View.VISIBLE);
    }

    public void toggleBottomActionBar() {
        boolean hasNoAudio = MusicUtils.getCurrentAudioId() == -1 ? true
                : false;
        boolean isPanelExpanded = mSlidingUpPanelLayout.isPanelExpanded();
        boolean isBottomActionBarParentVisibility = mBottomActionBarParent
                .getVisibility() == View.VISIBLE;
        boolean isGone = (isPanelExpanded || hasNoAudio)
                && isBottomActionBarParentVisibility;
        mBottomActionBarParent.setVisibility(isGone ? View.GONE
                : View.VISIBLE);
        mSlidingUpPanelLayout.setPanelHeight(isGone ? 0
                : mSlidingUpPanelLayoutHeight);

    }

    public ServiceToken getServiceToken() {
        return mToken;
    }

    /**
     * @return The resource ID to be inflated.
     */
    public abstract int setContentView();

    /**
     * Used to monitor the state of playback
     */
    private final static class PlaybackStatus extends BroadcastReceiver {

        private final WeakReference<BaseActivity> mReference;

        /**
         * Constructor of <code>PlaybackStatus</code>
         */
        public PlaybackStatus(final BaseActivity activity) {
            mReference = new WeakReference<BaseActivity>(activity);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void onReceive(final Context context, final Intent intent) {
            final String action = intent.getAction();
            if (action.equals(MusicPlaybackService.META_CHANGED)) {
                // Current info
                mReference.get().updateBottomActionBarInfo();
                // Update the favorites icon
                mReference.get().invalidateOptionsMenu();
                // Let the listener know to the meta chnaged
                for (final MusicStateListener listener : mReference.get().mMusicStateListener) {
                    if (listener != null) {
                        listener.onMetaChanged();
                    }
                }
            } else if (action.equals(MusicPlaybackService.PLAYSTATE_CHANGED)) {
                // Set the play and pause image
                mReference.get().mPlayPauseButton.updateState();
            } else if (action.equals(MusicPlaybackService.REPEATMODE_CHANGED)
                    || action.equals(MusicPlaybackService.SHUFFLEMODE_CHANGED)) {
                // Set the repeat image
                mReference.get().mRepeatButton.updateRepeatState();
                // Set the shuffle image
                mReference.get().mShuffleButton.updateShuffleState();
            } else if (action.equals(MusicPlaybackService.REFRESH)) {
                // Let the listener know to update a list
                for (final MusicStateListener listener : mReference.get().mMusicStateListener) {
                    if (listener != null) {
                        listener.restartLoader();
                    }
                }
            }
        }
    }
}
